import { isEqual } from "lodash-es";
import type { ModalMethods, ModalProps, ReturnMethods, UseModalInnerReturnType, UseModalReturnType } from "../typing";
import { isProdMode } from "@/utils/env";
import { isFunction } from "@/utils/is";
import { useEffect, useMemo, useRef, useState } from "react";
import { nextTick } from "@/utils";

const dataTransfer: any = {};
const openData: { [key: number]: boolean } = {};

function useStoreData(callback?: Fn) {
	const [drawerData, setDrawerData] = useState<any>({});

	useEffect(() => {
		callback && callback();
	}, [drawerData, callback]);

	return { drawerData, setDrawerData };
}

/**
 * @description: Applicable to independent modal and call outside
 */
export function useModal(): UseModalReturnType {
	const modal = useRef<Nullable<ModalMethods>>(null);
	const loaded = useRef<Nullable<boolean>>(false);
	const uid = useRef<number>(0);
	const { setDrawerData } = useStoreData();

	function register(modalMethod: ModalMethods, uuid: number) {
		uid.current = uuid;
		if (loaded.current && isProdMode() && modalMethod === modal.current) return;

		modal.current = modalMethod;
		loaded.current = true;
		modalMethod.emitOpen = (open: boolean, uid: number) => {
			openData[uid] = open;
		};
	}

	const getInstance = () => {
		const instance = modal.current;
		if (!instance) console.error("useModal instance is undefined!");

		return instance;
	};

	const methods: ReturnMethods = {
		setModalProps: (props: Partial<ModalProps>): void => {
			getInstance()?.setModalProps(props);
		},

		getOpen: useMemo((): boolean => {
			return openData[~~uid];
		}, [uid.current]),

		redoModalHeight: () => {
			getInstance()?.redoModalHeight?.();
		},

		openModal: <T = any>(open = true, data?: T, openOnSet = true): void => {
			getInstance()?.setModalProps({
				open
			});

			if (!data) return;
			const id = uid.current;
			if (openOnSet) {
				dataTransfer[id] = null;
				dataTransfer[id] = data;
				setDrawerData({
					...dataTransfer,
					[uid.current]: data
				});
				return;
			}
			const equal = isEqual(dataTransfer[id], data);
			if (!equal) dataTransfer[id] = data;
		},

		closeModal: () => {
			getInstance()?.setModalProps({ open: false });
		}
	};
	return [register, methods];
}

export function useModalInner(emitFunc: Fn, callbackFn?: Fn): UseModalInnerReturnType {
	const modalInstanceRef = useRef<Nullable<ModalMethods>>(null);
	const uidRef = useRef<number>(0);

	useStoreData(async () => {
		const data = dataTransfer[uidRef.current];
		if (!data) return;
		if (!callbackFn || !isFunction(callbackFn)) return;
		await nextTick();
		callbackFn(data);
	});

	const getInstance = () => {
		const instance = modalInstanceRef.current;
		if (!instance) console.error("useModalInner instance is undefined!");

		return instance;
	};

	const register = (modalInstance: ModalMethods, uuid: number) => {
		uidRef.current = uuid;
		modalInstanceRef.current = modalInstance;
		// like emit of Vue
		emitFunc && emitFunc(modalInstance, uuid);
	};

	return [
		register,
		{
			changeLoading: (loading = true) => {
				getInstance()?.setModalProps({ loading });
			},
			getOpen: useMemo((): boolean => {
				return openData[~~uidRef];
			}, [uidRef.current]),

			changeOkLoading: (loading = true) => {
				getInstance()?.setModalProps({ confirmLoading: loading });
			},

			closeModal: () => {
				getInstance()?.setModalProps({ open: false });
			},

			setModalProps: (props: Partial<ModalProps>) => {
				getInstance()?.setModalProps(props);
			},

			redoModalHeight: () => {
				const callRedo = getInstance()?.redoModalHeight;
				callRedo && callRedo();
			}
		}
	];
}
